
#include <locale>
#include <cstdio>
#include <BFile>
#include <BSocket>
#include <BThread>
#include <BReadWrite>
#include <BStyleDocument>
#include <BApplication>
#include "member_BWidget.h"
#include "member_BStyle.h"
#include "member_BThread.h"
#include "IGlobal.h"

#ifdef linux
#include <errno.h>
#include <dlfcn.h>
#include <pthread.h>
#endif

using namespace BWE;

BApplication::BApplication(int argc, char** argv)
{
#ifdef linux
	if (app_thread_id)
		return;
	app_thread_id = pthread_self();
#endif

#ifdef _WIN32
	if (app_thread_id)
		return;
	app_thread_id = GetCurrentThreadId();
#endif
	app_threads.append(0);

	app_done = false;
	app_argc = argc;

	app_style = new BStyle(BColor(255, 255, 255));

	App_InitGlobalMutex();

	app_extAlias.insert("tif", "tiff");
	app_extAlias.insert("jpg", "jpeg");
	app_extAlias.insert("wav", "wave");
	app_extAlias.insert("stp", "step");
	app_extAlias.insert("glb", "gltf");

	setlocale(LC_ALL, "");

#ifdef linux
	app_homePath = getenv("HOME");
	if (argc > 0 && argv)
	{
		for (int i = 0; i < argc; i++)
		{
			app_argv.append(argv[i]);
		}
	}

#ifdef BWE_DEBUG
	const char* libName = "libBWEd.so";
#else
	const char* libName = "libBWE.so";
#endif
	char cstr[256];
	void* handle = dlopen(libName, RTLD_NOW);
	int ret = dlinfo(handle, RTLD_DI_ORIGIN, cstr);
	if (ret == 0)
	{
		app_libPath = cstr;
	}
	if (!app_display)
	{
		app_display = XOpenDisplay(0);
	}
#endif

#ifdef _WIN32
	wchar_t wstr[256];
	char cstr[256];
	if (argc > 0 && argv)
	{
		for (int i = 0; i < argc; i++)
		{
			mbstowcs(wstr, argv[i], 256);
			bWcsToMbs(wstr, cstr);
			app_argv.append(cstr);
		}
	}

#ifdef BWE_DEBUG
	const wchar_t* libName = L"BWEd.dll";
#else
	const wchar_t* libName = L"BWE.dll";
#endif
	HMODULE module = GetModuleHandle(libName);
	DWORD len = GetModuleFileName(module, wstr, 256);
	bWcsToMbs(wstr, cstr);
	if (len > 0)
	{
		app_libPath = cstr;
		app_libPath.replace('\\', '/');
		app_libPath -= '/';
	}
	len = GetEnvironmentVariable(L"USERPROFILE", wstr, 256);
	bWcsToMbs(wstr, cstr);
	if (len > 0)
	{
		app_homePath = cstr;
		app_homePath.replace('\\', '/');
		app_homePath -= '/';
	}
	bWcsToMbs(libName, cstr);
	app_libPath -= cstr;
#endif
	if (app_libPath.empty())
	{
		puts("Locate library path failed.");
	}

	if (argc > 0 && argv)
	{
		for (int i = 0; i < app_argv.size(); i++)
		{
			app_argv[i].replace('\\', '/');
		}
		app_appPath = app_argv[0];
		app_appPath -= app_appPath.name();
		app_appPath -= '/';
	}

	app_font = new BFont("msyh", 16);
}
BApplication::~BApplication()
{
	app_windows.clear();
	app_sockets.clear();
	app_slotPackMap.clear();
	app_signalWaitMap.clear();
}

int BApplication::argc()
{
	return app_argc;
}
const BString& BApplication::argv(int index)
{
	return app_argv(index);
}

void BApplication::setFrequency(int frequency)
{
	if (app_frequency <= 0)
		app_frequency = 30;
	else
		app_frequency = frequency;
}
int BApplication::frequency()
{
	return app_frequency;
}
int BApplication::realFrequency()
{
	return app_realFrequency;
}

void BApplication::setClickInterval(BReal clickInterval)
{
	clickInterval = bClamp(clickInterval, 0.1, 5.0);
	if (app_clickInterval != clickInterval)
	{
		app_clickInterval = clickInterval;
	}
}
BReal BApplication::clickInterval()
{
	return app_clickInterval;
}

void BApplication::setTipsDelay(BReal tipsDelay)
{
	tipsDelay = bClamp(tipsDelay, BReal(0), BReal(100));
	if (app_tipsDelay != tipsDelay)
	{
		app_tipsDelay = tipsDelay;
	}
}
BReal BApplication::tipsDelay()
{
	return app_tipsDelay;
}

void BApplication::setCoreWindow(BWidget* widget)
{
	app_coreWidget = widget;
}
BWidget* BApplication::coreWidget()
{
	return app_coreWidget;
}

void BApplication::exit(int code)
{
	app_code = code;
	app_done = true;
}
int BApplication::code()
{
	return app_code;
}

int BApplication::execute()
{
	while (!app_done)
	{
		App_CycleBegin();
		App_Event();
		App_Signal();
		App_Update();
		App_Frame();
		App_CycleEnd();
	}
	return app_code;
}

const BString& BApplication::appPath()
{
	return app_appPath;
}
const BString& BApplication::libPath()
{
	return app_libPath;
}
const BString& BApplication::homePath()
{
	return app_homePath;
}

BString BApplication::currentPath()
{
	BString path;
	currentPath(path);
	return path;
}
bool BApplication::currentPath(BString& path)
{
	char buffer[256];
	buffer[0] = 0;
#ifdef linux
	if (NULL == getcwd(buffer, 256))
	{
		fprintf(stderr, "getcwd error: %s", strerror(errno));
	}
#endif

#ifdef _WIN32
	wchar_t wstr[256];
	DWORD len0 = GetCurrentDirectory(256, wstr);
	int len = bWcsToMbs(wstr, buffer);
	for (int i = 0; i < len; i++)
	{
		if (buffer[i] == '\\')
			buffer[i] = '/';
	}
#endif
	path = buffer;
	return true;
}
bool BApplication::setCurrentPath(const BString& path)
{
	if (path.empty())
		return true;

#ifdef linux
	int ret = chdir(path.cstr());
	return ret > -1;
#endif

#ifdef _WIN32
	if (path.hasUtf(8))
	{
		wchar_t wstr[256];
		bMbsToWcs(path.cstr(), wstr);
		return SetCurrentDirectory(wstr) == TRUE;
	}
	BOOL ret = SetCurrentDirectoryA(path.cstr());
	return ret == TRUE;
#endif
}

void BApplication::setFont(const BString& family, int size)
{
	BFont* font = new BFont(family, size);
	setFont(font);
}
void BApplication::setFont(BFont* font)
{
	app_font = font;
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (BWidget* widget = window->boss)
		{
			widget_member(widget)->dirtyStyle = true;
			widget_member(widget)->refresh = true;
		}
	}
}
const BFont* BApplication::font()
{
	return app_font;
}

void BApplication::setStyle(BStyle* style)
{
	if (app_style != style)
	{
		app_style = style;
	}
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (BWidget* widget = window->boss)
		{
			widget_member(widget)->dirtyStyle = true;
			widget_member(widget)->refresh = true;
		}
	}
}
BStyle* BApplication::style()
{
	return app_style;
}

void BApplication::setStyleSheet(const BString& sheet)
{
	app_style = new BStyle(BColor(255, 255, 255));
	if (sheet.empty())
		return;

	BStyleDocument document;
	if (BFile::Exist(sheet))
	{
		document.load(sheet, app_style);
		printf("Load style sheet %s", sheet.cstr());
	}
	else if (app_resources.contain(sheet))
	{
		IResource& resource = app_resources(sheet);
		document.setPath(sheet.path());
		document.read(resource, app_style);
		printf("Load style sheet %s", sheet.cstr());
	}
	else
	{
		document.read(sheet, app_style);
		printf("Read style sheet");
	}
	if (document.message().size())
		printf(" message :\n%s\n", document.message().cstr());
	else
		printf(" success.\n");
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (BWidget* widget = window->boss)
		{
			widget_member(widget)->dirtyStyle = true;
			widget_member(widget)->refresh = true;
		}
	}
}
void BApplication::addStyleSheet(const BString& sheet)
{
	if (sheet.empty())
	{
		return;
	}
	BStyleDocument document;
	if (BFile::Exist(sheet))
	{
		document.load(sheet, app_style);
		printf("Load style sheet %s", sheet.cstr());
	}
	else if (app_resources.contain(sheet))
	{
		IResource& resource = app_resources(sheet);
		document.read(resource, app_style);
		printf("Load style sheet %s", sheet.cstr());
	}
	else
	{
		document.read(sheet, app_style);
		printf("Read style sheet");
	}
	if (document.message().size())
		printf(" message :\n%s\n", document.message().cstr());
	else
		printf(" success.\n");
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (BWidget* widget = window->boss)
		{
			widget_member(widget)->dirtyStyle = true;
			widget_member(widget)->refresh = true;
		}
	}
}

void BApplication::resetStyles(BWidget* widget)
{
	if (widget)
	{
		BWidgetHolderArray& children = widget_member(widget)->children;
		for (int i = 0; i < children.size(); i++)
		{
			BWidget* child = children[i];
			resetStyles(child);
		}
		if (BStyle* style = widget_member(widget)->style)
		{
			style->reset();
			widget_member(widget)->dirtyStyle = true;
		}
		if (BStyle* style = widget_member(widget)->realStyle)
		{
			style->reset();
			widget_member(widget)->dirtyStyle = true;
		}
	}
	else
	{
		for (int i = 0; i < app_windows.size(); i++)
		{
			IWindow* window = app_windows[i];
			if (BWidget* widget = window->boss)
			{
				resetStyles(widget);
			}
		}
		app_style = new BStyle(BColor(255, 255, 255));
		for (int i = 0; i < app_windows.size(); i++)
		{
			IWindow* window = app_windows[i];
			if (BWidget* widget = window->boss)
			{
				widget_member(widget)->dirtyStyle = true;
				widget_member(widget)->refresh = true;
			}
		}
	}
}
void BApplication::cleanTranslation(BWidget* widget)
{
	if (widget)
	{
		for (int i = 0; i < widget_member(widget)->children.size(); i++)
		{
			BWidget* child = widget_member(widget)->children[i];
			cleanTranslation(child);
		}
		if (BStyle* style = widget_member(widget)->style)
		{
			style->cleanTranslation();
			widget_member(widget)->dirtyStyle = true;
		}
		if (BStyle* style = widget_member(widget)->realStyle)
		{
			style->cleanTranslation();
			widget_member(widget)->dirtyStyle = true;
		}
	}
	else
	{
		for (int i = 0; i < app_windows.size(); i++)
		{
			IWindow* window = app_windows[i];
			if (BWidget* widget = window->boss)
			{
				cleanTranslation(widget);
			}
		}
		app_style->cleanTranslation();
		for (int i = 0; i < app_windows.size(); i++)
		{
			IWindow* window = app_windows[i];
			if (BWidget* widget = window->boss)
			{
				widget_member(widget)->dirtyStyle = true;
				widget_member(widget)->refresh = true;
			}
		}
	}
}

bool BApplication::setProperty(const char* name, const BValue& value)
{
	if (!name)
		return false;
	if (value.empty())
	{
		app_propertys.remove(name);
		return true;
	}
	auto it = app_propertys.insert(name);
	if (*it != value)
	{
		*it = value;
	}
	return true;
}
const BValue& BApplication::property(const char* name)
{
	return app_propertys(name);
}

bool BApplication::setProperty(const BString& name, const BValue& value)
{
	if (name.empty())
		return false;
	if (value.empty())
	{
		app_propertys.remove(name);
		return true;
	}
	auto it = app_propertys.insert(name);
	if (*it != value)
	{
		*it = value;
	}
	return true;
}
const BValue& BApplication::property(const BString& name)
{
	return app_propertys(name);
}

int BApplication::socketCount()
{
	return app_sockets.size();
}
BSocket* BApplication::socket(int index)
{
	return app_sockets.at(index);
}

int BApplication::sceneCount()
{
	return app_scenes.size();
}
BScene* BApplication::scene(int index)
{
	return app_scenes.at(index);
}

int BApplication::movieCount()
{
	return app_movies.size();
}
BMovie* BApplication::movie(int index)
{
	return app_movies.at(index);
}

int BApplication::shortcutCount()
{
	return app_shortcuts.size();
}
BShortcut* BApplication::shortcut(int index)
{
	return app_shortcuts.at(index);
}

int BApplication::threadCount()
{
	return app_threads.size();
}
BThread* BApplication::thread(int index)
{
	if (index == 0 && app_threads(0) == 0)
	{
		BThread* thread = new MainThread();
#ifdef linux
		thread_member(thread)->thread_id = app_thread_id;
#endif
#ifdef _WIN32
		HANDLE handle = OpenThread(THREAD_ALL_ACCESS, false, app_thread_id);
		thread_member(thread)->handle = handle;
		thread_member(thread)->thread_id = app_thread_id;
#endif
		app_threads[0] = thread;
	}
	return app_threads(index);
}

void BApplication::addPluginPath(const BString& pluginPath)
{
	if (!app_pluginPaths.contain(pluginPath))
	{
		app_pluginPaths.append(pluginPath);
	}
}
void BApplication::removePluginPath(const BString& pluginPath)
{
	app_pluginPaths.remove(pluginPath);
}

void BApplication::addFontPath(const BString& fontPath)
{
	if (!app_fontPaths.contain(fontPath))
	{
		app_fontPaths.append(fontPath);
	}
}
void BApplication::removeFontPath(const BString& fontPath)
{
	app_fontPaths.remove(fontPath);
}

const BString& BApplication::fontPath(int index)
{
	return app_fontPaths(index);
}
int BApplication::fontPathCount()
{
	return app_fontPaths.size();
}

int BApplication::pluginPathCount()
{
	return app_pluginPaths.size();
}
const BString& BApplication::pluginPath(int index)
{
	return app_pluginPaths(index);
}

BWidget* BApplication::activeWindow()
{
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (window->boss && window->boss->visible() && window->boss->actived())
		{
			return window->boss;
		}
	}
	return 0;
}
BWidget* BApplication::hoveredWidget()
{
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (window->boss && window->boss->visible() && window->hoverWidget)
		{
			return window->hoverWidget;
		}
	}
	return 0;
}
BWidget* BApplication::focusedWidget()
{
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (window->boss && window->boss->visible() && window->focusWidget)
		{
			return window->focusWidget;
		}
	}
	return 0;
}
BWidget* BApplication::activeModalWidget()
{
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (window->boss && window->boss->visible())
		{
			BWidget* popupWidget = window->popupWidgets.last();
			if (popupWidget && popupWidget->modal())
				return popupWidget;
		}
	}
	return 0;
}
BWidget* BApplication::activePopupWidget()
{
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (window->boss && window->boss->visible())
		{
			BWidget* popupWidget = window->popupWidgets.last();
			if (popupWidget)
				return popupWidget;
		}
	}
	return 0;
}
